<script setup lang="ts">
import type { ApiPagination, ApiPaginationParams } from '#types/utils'

import type { CSSProperties } from 'vue'
import { computed, nextTick, ref, unref, watch } from 'vue'

import { IconifyIcon } from '@vben/icons'

import { uniqueByField } from '@vben/utils'

import type { VbenFormProps } from '@vben/common-ui'
import type { VxeGridProps } from '@vben/plugins/vxe-table'
// 全局组件不允许使用 '#/adapter/vxe-table' 下的 useVbenVxeGrid 否则表单会报组件未注册
import { $t } from '@vben/locales'
import { useVbenVxeGrid } from '@vben/plugins/vxe-table'
import { useToggle } from '@vueuse/core'
import { Popover, Tag } from 'ant-design-vue'

interface Props {
    /**
     * 已经选中的实体
     */
    options?: Array<Record<string, any>>

    /**
     * 主键
     */
    rowKey?: string
    /**
     * 主键
     */
    labelKey?: string
    columns?: VxeGridProps['columns']
    width?: string
    // 是否多选
    multiple?: boolean
    modelValue: Array<number | string> | null | (number | string) | undefined
    formOptions?: Partial<VbenFormProps>
    gridOptions?: Partial<VxeGridProps>
    api?: (params: ApiPaginationParams<Record<string, any>>) => Promise<ApiPagination<any>>
    maxShowLength?: number
    disabled?: boolean
    height?: number
    placeholder?: string
    selectWidth?: CSSProperties['width']
    showKey?: string
}

const props = withDefaults(defineProps<Props>(), {
    options: () => [],
    rowKey: 'id',
    columns: () => [
        {
            title: $t('common.name'),
            field: 'name'
        }
    ],
    width: '500px',
    selectWidth: '100%',
    multiple: true,
    labelKey: 'name',
    formOptions: () => {
        return {}
    },
    gridOptions: () => {
        return {}
    },
    api: undefined,
    maxShowLength: 1,
    disabled: false,
    height: 400,
    placeholder: '',
    showKey: ''
})

const emit = defineEmits<{
    (
        event: 'update:modelValue',
        value: Array<number | string> | null | (number | string) | undefined
    ): void
    (event: 'updateLabel', label: number | string): void
}>()

const formOptions: VbenFormProps = {
    // 默认展开
    collapsed: false,
    // 控制表单是否显示折叠按钮
    showCollapseButton: true,
    collapsedRows: 1,
    // 按下回车时是否提交表单
    submitOnEnter: true,
    resetButtonOptions: {
        show: false
    },
    ...props.formOptions
}

const proxyConfig: VxeGridProps['proxyConfig'] = props.gridOptions?.proxyConfig || {}

const showKeyMix = computed(() => {
    return props.showKey || props.labelKey
})

const apiFn = props.api

if (apiFn && !proxyConfig?.ajax?.query) {
    proxyConfig.ajax = {
        query: async ({ page }: any, formValues: any) => {
            const res = await apiFn({
                ...formValues,
                limit: page.pageSize,
                page: page.currentPage
            })
            setTimeout(() => {
                initCheckBox()
            }, 100)
            return {
                items: res.records,
                total: res.total
            }
        },
        ...props.gridOptions?.proxyConfig
    }
}

const gridOptions: VxeGridProps<any> = {
    height: props.height,

    checkboxConfig: {
        highlight: true,
        range: true,
        trigger: 'row',
        reserve: true
    },
    radioConfig: {
        highlight: true,
        trigger: 'row',
        reserve: true
    },
    pagerConfig: {
        pageSize: 20,
        layouts: ['PrevPage', 'Number', 'NextPage']
    },
    ...props.gridOptions,
    columns: [
        {
            align: 'center',
            title: props.multiple ? '' : '',
            type: props.multiple ? 'checkbox' : 'radio',
            width: 50
        },
        ...props.columns
    ],
    proxyConfig
}

const [Grid, GridApi] = useVbenVxeGrid({
    formOptions,
    gridOptions,
    gridEvents: {
        checkboxChange,
        radioChange,
        checkboxAll
    },
    showSearchForm: formOptions?.schema ? unref(formOptions.schema).length > 0 : false
})
// const isTablePopoverShow = ref(false)

const [isTablePopoverShow, toggleTablePopover] = useToggle()

const localOptions = ref<Array<Record<string, any>>>([])

const optionsFix = computed(() => {
    const { options, rowKey } = props

    return uniqueByField([...options, ...localOptions.value], rowKey)
})

// 全部选中的内容
const curSelectedOptions = computed(() => {
    return optionsFix.value.filter((item) => {
        const { modelValue } = props
        const val = item[props.rowKey]
        return Array.isArray(modelValue) ? modelValue.includes(val) : val === modelValue
    })
})

// 可以显示的内容
const showSelectedOptions = computed(() => {
    return curSelectedOptions.value.slice(0, props.maxShowLength)
})

// 剩余显示长度
const restShowLength = computed(() => {
    const { maxShowLength } = props
    return Math.max(0, curSelectedOptions.value.length - (maxShowLength || 0))
})

const placeholderMix = computed(() => {
    return props.placeholder || $t('common.form.select')
})

// 显示状态才能活动table的api
watch(
    () => props.modelValue && isTablePopoverShow.value,
    () => {
        initCheckBox()
    },
    { immediate: true }
)

function initCheckBox() {
    if (isTablePopoverShow.value) {
        nextTick(() => {
            const val = props.modelValue

            clearCheckboxRow()
            setCheckboxRow(val)
        })
    }
}

function clearCheckboxRow() {
    const grid = GridApi?.grid
    if (!grid.getTableData) return
    if (props.multiple) {
        grid.clearCheckboxRow()
    } else {
        grid.clearRadioRow()
    }
}

function setCheckboxRow(val: Props['modelValue']) {
    const grid = GridApi?.grid

    if (!grid.getTableData) return

    const { tableData } = grid.getTableData()

    if (tableData.length === 0) return

    const arr = Array.isArray(val) ? val : [val]

    const rows = tableData.filter((item) => arr.includes(item[props.rowKey]))
    if (props.multiple) {
        grid.setCheckboxRow(rows, true)
    } else {
        grid.setRadioRow(rows[0])
    }
}

function checkboxChange({ row, checked }: any) {
    const { modelValue, multiple } = props

    if (multiple) {
        const val = row[props.rowKey]

        const arr = modelValue ? (Array.isArray(modelValue) ? modelValue : [modelValue]) : []
        if (checked) {
            if (arr.includes(val)) {
                return
            }
            arr.push(val)
            updateLocalValue(row)
        } else {
            const index = arr.indexOf(val)

            if (index === -1) {
                return
            }
            arr.splice(index, 1)
        }

        emit('update:modelValue', arr)
    }
}

// 全选和取消全选
function checkboxAll(item: any) {
    const { checked, records } = item
    if (checked) {
        records.forEach((v: any) => {
            checkboxChange({ row: v, checked })
        })
    } else {
        const grid = GridApi?.grid

        if (!grid.getTableData) return

        const { tableData } = grid.getTableData()

        tableData.forEach((v: any) => {
            checkboxChange({ row: v, checked })
        })
    }
}

// 单选值改变
function radioChange({ row }: any) {
    const val = row[props.rowKey]

    const { modelValue, labelKey } = props

    if (val === modelValue) return

    if (row) {
        updateLocalValue(row)
    }

    emit('updateLabel', row[labelKey] || '')
    emit('update:modelValue', val)
    toggleTablePopover(false)
}

function updateLocalValue(row: any) {
    localOptions.value = uniqueByField([...localOptions.value, row], props.rowKey)
}

async function delTag(val: number | string) {
    if (!props.multiple) {
        toggleTablePopover(true)
        await nextTick()
    }
    const grid = GridApi?.grid
    const item = optionsFix.value.find((v) => v[props.rowKey] === val)

    if (item) {
        if (props.multiple) {
            checkboxChange({ row: item, checked: false })
        } else {
            radioChange({ row: item, checked: false })
            emit('updateLabel', '')
            emit('update:modelValue', '')
            if (Object.keys(grid).length > 0) {
                grid.clearCheckboxRow()
            }
        }
    }
    initCheckBox()
}

function handleOpenChange(visible: boolean) {
    toggleTablePopover(!props.disabled && visible)
}
</script>
<template>
    <Popover
        :content-props="{ align: 'end', alignOffset: -11, sideOffset: 8 }"
        :open="isTablePopoverShow && !disabled"
        content-class="p-0 pt-3 "
        trigger="click"
        @open-change="handleOpenChange"
    >
        <template #content>
            <div :style="{ width }">
                <Grid />
            </div>
        </template>

        <div
            :class="{
                'ant-input-disabled': disabled
            }"
            class="table-select-box flex items-center"
            :style="{
                width: props.selectWidth
            }"
        >
            <div class="flex flex-1">
                <div style="min-height: 22px">
                    <div v-if="showSelectedOptions.length === 0" class="text-gray-400">
                        {{ placeholderMix }}
                    </div>

                    <Tag
                        v-for="item in showSelectedOptions"
                        :key="item[props.rowKey]"
                        :closable="!disabled"
                        @close="delTag(item[props.rowKey])"
                    >
                        {{ item[showKeyMix] }}
                    </Tag>
                </div>

                <div v-if="restShowLength > 0" class="flex-1">
                    <Popover
                        :content-props="{ align: 'end', alignOffset: -11, sideOffset: 8 }"
                        content-class="p-0 pt-3 "
                        trigger="hover"
                    >
                        <template #content>
                            <div class="mb-3 border-b pb-1">当前已选择：</div>
                            <div
                                class="flex flex-wrap gap-2"
                                style="width: 300px; max-height: 300px; overflow-y: auto"
                            >
                                <Tag
                                    v-for="item in curSelectedOptions"
                                    :key="item[props.rowKey]"
                                    :closable="!disabled"
                                    color="processing"
                                    @close="delTag(item[props.rowKey])"
                                >
                                    {{ item[showKeyMix] }}
                                </Tag>

                                <div v-if="curSelectedOptions.length === 0">暂无选中数据</div>
                            </div>
                        </template>
                        <Tag class="cursor-pointer">+{{ restShowLength }}...</Tag>
                    </Popover>
                </div>
            </div>
            <IconifyIcon
                class="cursor-pointer"
                color="#ccc"
                height="12"
                icon="ant-design:down-outlined"
            />
        </div>
    </Popover>
</template>

<style lang="less" scoped>
.table-select-box {
    position: relative;
    display: inline-flex;
    width: 100%;
    min-width: 0;
    padding: 4px 11px;
    color: rgba(9, 9, 11, 0.88);
    font-size: 14px;
    line-height: 1.5714285714285714;
    background-color: hsl(0 0% 100%);
    background-image: none;
    border-width: 1px;
    border-style: solid;
    border-color: hsl(240 5.9% 90%);
    border-radius: 8px;
    transition: all 0.2s;
    cursor: pointer;

    &.ant-input-disabled {
        background-color: rgba(9, 9, 11, 0.04);
        cursor: default;
    }
}
</style>
